// table.js
import { fabric } from "fabric";

const createInitTable = () => {
  fabric.Table = fabric.util.createClass(fabric.Group, {
    type: "table",
    borderWidth: 1,
    rowHeights: [],
    columnWidths: [],
    cells: [],
    resizingHandleSize: 5,
    initialize: function (tableOptions) {
      const { left, top } = tableOptions;
      this.callSuper("initialize", this.generateCellsAndHandles(tableOptions), {
        subTargetCheck: true,
        objectCaching: false,
        left,
        top,
      });

      // 绑定事件监听
      this.on("mousedown", this.handleMouseDown.bind(this));
    },

    generateCellsAndHandles: function (tableOptions) {
      const { borderWidth, rowHeights, columnWidths, cells } = tableOptions;
      const rectOptions = {
        rx: 0,
        stroke: "#000",
        fill: "transparent",
        shadow: 0,
        strokeWidth: +borderWidth,
        strokeUniform: true,
      };

      const textOptions = {
        lineHeight: 1,
        fontSize: 18,
        stroke: "#000",
        fill: "#000",
        selectable: false,
        textAlign: "left",
        editingBorderColor: "#FF9002",
      };

      const cellCmps = [];
      const ignore = [];
      let totalH = 0;

      // 生成单元格
      for (let i = 0; i < rowHeights.length; i++) {
        let totalW = 0;
        for (let j = 0; j < columnWidths.length; j++) {
          if (ignore.includes(`${i}:${j}`)) {
            totalW += columnWidths[j];
            continue;
          }

          let cellData =
            cells.find((cell) => cell.row === i && cell.col === j) || {};
          let rowSpan = Math.min(
            cellData.rowSpan || 0,
            rowHeights.length - i - 1
          );
          let colSpan = Math.min(
            cellData.colSpan || 0,
            columnWidths.length - j - 1
          );

          let width = columnWidths
            .slice(j, j + colSpan + 1)
            .reduce((a, b) => a + b, 0);
          let height = rowHeights
            .slice(i, i + rowSpan + 1)
            .reduce((a, b) => a + b, 0);

          // 标记被跨单元格覆盖的位置
          for (let rs = 0; rs <= rowSpan; rs++) {
            for (let cs = 0; cs <= colSpan; cs++) {
              if (rs !== 0 || cs !== 0) {
                ignore.push(`${i + rs}:${j + cs}`);
              }
            }
          }

          const cell = new fabric.TableCell(
            {
              ...rectOptions,
              left: totalW,
              top: totalH,
              width,
              height,
              row: i,
              col: j,
              rowSpan,
              colSpan,
            },
            {
              ...textOptions,
              left: totalW + rectOptions.strokeWidth,
              top: totalH + rectOptions.strokeWidth,
              width: width - rectOptions.strokeWidth * 2,
              height: height - rectOptions.strokeWidth * 2,
            },
            cellData.content || ""
          );
          cellCmps.push(cell);
          totalW += columnWidths[j];
        }
        totalH += rowHeights[i];
      }

      // 生成列宽调整手柄
      let accumulatedWidth = 0;
      for (let j = 0; j < columnWidths.length; j++) {
        const handle = new fabric.Rect({
          left:
            accumulatedWidth + columnWidths[j] - this.resizingHandleSize / 2,
          top: 0,
          width: this.resizingHandleSize,
          height: totalH,
          fill: "rgba(0,0,0,0)",
          hoverCursor: "col-resize",
          hasControls: false,
          hasBorders: false,
          selectable: false,
          evented: true,
          data: { type: "column", index: j },
        });
        cellCmps.push(handle);
        accumulatedWidth += columnWidths[j];
      }

      // 生成行高调整手柄
      let accumulatedHeight = 0;
      for (let i = 0; i < rowHeights.length; i++) {
        const handle = new fabric.Rect({
          left: 0,
          top: accumulatedHeight + rowHeights[i] - this.resizingHandleSize / 2,
          width: accumulatedWidth,
          height: this.resizingHandleSize,
          fill: "rgba(0,0,0,0)",
          hoverCursor: "row-resize",
          hasControls: false,
          hasBorders: false,
          selectable: false,
          evented: true,
          data: { type: "row", index: i },
        });
        cellCmps.push(handle);
        accumulatedHeight += rowHeights[i];
      }

      return cellCmps;
    },

    handleMouseDown: function (e) {
      const target = e.target;
      if (!target || !target.data) return;

      const canvas = this.canvas;
      const pointer = canvas.getPointer(e.e);

      if (target.data.type === "column") {
        this.currentResize = {
          type: "column",
          index: target.data.index,
          startX: pointer.x,
          originalWidth: this.columnWidths[target.data.index],
        };
      } else if (target.data.type === "row") {
        this.currentResize = {
          type: "row",
          index: target.data.index,
          startY: pointer.y,
          originalHeight: this.rowHeights[target.data.index],
        };
      }

      if (this.currentResize) {
        canvas.on("mouse:move", this.handleMouseMove.bind(this));
        canvas.on("mouse:up", this.handleMouseUp.bind(this));
      }
    },

    handleMouseMove: function (e) {
      if (!this.currentResize) return;

      const canvas = this.canvas;
      const pointer = canvas.getPointer(e.e);

      if (this.currentResize.type === "column") {
        const delta = pointer.x - this.currentResize.startX;
        const newWidth = Math.max(20, this.currentResize.originalWidth + delta);
        this.columnWidths[this.currentResize.index] = newWidth;
      } else if (this.currentResize.type === "row") {
        const delta = pointer.y - this.currentResize.startY;
        const newHeight = Math.max(
          20,
          this.currentResize.originalHeight + delta
        );
        this.rowHeights[this.currentResize.index] = newHeight;
      }

      // 更新表格
      const children = this.generateCellsAndHandles({
        borderWidth: this.borderWidth,
        rowHeights: this.rowHeights,
        columnWidths: this.columnWidths,
        cells: this.cells,
        left: this.left,
        top: this.top,
      });

      this.removeAll();
      this.add(...children);
      canvas.requestRenderAll();
    },

    handleMouseUp: function () {
      this.currentResize = null;
      this.canvas.off("mouse:move", this.handleMouseMove.bind(this));
      this.canvas.off("mouse:up", this.handleMouseUp.bind(this));
    },

    toObject: function () {
      return fabric.util.object.extend(this.callSuper("toObject"), {
        borderWidth: this.get("borderWidth"),
        rowHeights: this.get("rowHeights"),
        columnWidths: this.get("columnWidths"),
        cells: this.get("cells"),
      });
    },
  });

  // TableCell 定义
  fabric.TableCell = fabric.util.createClass(fabric.Group, {
    type: "tableCell",
    text: null,
    rect: null,
    initialize: function (rectOptions, textOptions, text) {
      this.rect = new fabric.Rect(rectOptions);
      this.text = new fabric.Textbox(text, {
        ...textOptions,
        selectable: false,
      });

      // 保存单元格位置信息
      this.row = rectOptions.row;
      this.col = rectOptions.col;
      this.rowSpan = rectOptions.rowSpan || 0;
      this.colSpan = rectOptions.colSpan || 0;

      this.setupEvents();
      this.callSuper("initialize", [this.rect, this.text], {
        subTargetCheck: true,
        objectCaching: false,
      });
    },

    setupEvents: function () {
      this.on("mousedblclick", () => {
        this.text.selectable = true;
        this.canvas.setActiveObject(this.text);
        this.text.enterEditing();
      });

      this.text.on("editing:exited", () => {
        this.text.selectable = false;
      });
    },

    toObject: function () {
      return fabric.util.object.extend(this.callSuper("toObject"), {
        text: this.text.get("text"),
      });
    },
  });

  // 反序列化方法
  fabric.Table.fromObject = function (o, callback) {
    const options = {
      left: o.left,
      top: o.top,
      rowHeights: o.rowHeights,
      columnWidths: o.columnWidths,
      borderWidth: o.borderWidth,
      cells: o.cells,
    };
    callback(new fabric.Table(options));
  };
};

export default createInitTable;
